#pragma once
#include <zGraphicsConfig.hpp>
#include <Image/Image.hpp>
#include "Texture.hpp"
#include <Utility/Tools.hpp>
#include "../../Graphics/Coordinate.hpp"

namespace zzz{
class ZGRAPHICS_CLASS TextureCube : public Texture
{
public:
  TextureCube(int newiformat=GL_RGB);
  TextureCube(zuint32 size, int newiformat);
  TextureCube(zuint32 width, zuint32 height, int newiformat);
  TextureCube(const string &filename, int newiformat);
  TextureCube(const string &PosX, const string &NegX, const string &PosY, 
        const string &NegY, const string &PosZ, const string &NegZ, 
        int newiformat);
  void Create(zuint32 size);
  void Create(zuint width, zuint height);
  void Create(const string &filename);
  void Create(const string &PosX, const string &NegX, const string &PosY, 
        const string &NegY, const string &PosZ, const string &NegZ);
  template<typename T> bool ImageToTexture(const Image<T> &PosX,const Image<T> &NegX,const Image<T> &PosY,
                       const Image<T> &NegY,const Image<T> &PosZ,const Image<T> &NegZ);
  template<typename T> bool TextureToImage(Image<T> &PosX,Image<T> &NegX,Image<T> &PosY,
                       Image<T> &NegY,Image<T> &PosZ,Image<T> &NegZ);

  bool Bind(GLenum bindto=GL_TEXTURE0);

  void ChangeInternalFormat(int newiformat);
  void ChangeSize(zuint32 width,zuint32 height);
  void ChangeSize(zuint32 size);
  void ChangeParameter(GLenum filter,GLenum wrap);

  bool DrawCubemap(float size=1);
  
  zuint width_,height_;
private:
  bool ReadPfm(const string &filename);
  bool SavePfm(const string &filename);
};

template<typename T>
bool TextureCube::ImageToTexture(const Image<T> &PosX,const Image<T> &NegX,const Image<T> &PosY,
                const Image<T> &NegY,const Image<T> &PosZ,const Image<T> &NegZ)
{
  glBindTexture(GL_TEXTURE_CUBE_MAP,GetID());
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, filter_para_);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, filter_para_);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, wrap_para_);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, wrap_para_);

  width_=PosX.Cols();
  height_=PosX.Rows();
  int format=PosX.Format_;
  int type=PosY.Type_;
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X,0,internal_format_,width_,height_,0,format,type,(const GLvoid *)PosX.Data());
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X,0,internal_format_,width_,height_,0,format,type,(const GLvoid *)NegX.Data());
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y,0,internal_format_,width_,height_,0,format,type,(const GLvoid *)PosY.Data());
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,0,internal_format_,width_,height_,0,format,type,(const GLvoid *)NegY.Data());
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z,0,internal_format_,width_,height_,0,format,type,(const GLvoid *)PosZ.Data());
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,0,internal_format_,width_,height_,0,format,type,(const GLvoid *)NegZ.Data());
  CHECK_GL_ERROR()
  return true;
}

template<typename T>
bool TextureCube::TextureToImage(Image<T> &PosX,Image<T> &NegX,Image<T> &PosY,
                 Image<T> &NegY,Image<T> &PosZ,Image<T> &NegZ)
{
  GLint lastTex;
  glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP,&lastTex);

  int format=PosX.Format_;
  int type=PosY.Type_;

  PosX.SetSize(height_,width_);
  NegX.SetSize(height_,width_);
  PosY.SetSize(height_,width_);
  NegY.SetSize(height_,width_);
  PosZ.SetSize(height_,width_);
  NegZ.SetSize(height_,width_);

  glBindTexture(GL_TEXTURE_CUBE_MAP,GetID());

  glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X,0,format,type,(GLvoid *)PosX.Data());
  glGetTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_X,0,format,type,(GLvoid *)NegX.Data());
  glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Y,0,format,type,(GLvoid *)PosY.Data());
  glGetTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,0,format,type,(GLvoid *)NegY.Data());
  glGetTexImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Z,0,format,type,(GLvoid *)PosZ.Data());
  glGetTexImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,0,format,type,(GLvoid *)NegZ.Data());

  glBindTexture(GL_TEXTURE_CUBE_MAP,lastTex);
  CHECK_GL_ERROR()
  return true;
}

}
